﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Configuration.Provider;
using System.Data;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web.Configuration;
using System.Web.Security;
using Microsoft.SharePoint;

namespace ProjectHT.YKDGVB2012.Core.Membership
{
    public class CsMembershipProvider : MembershipProvider
    {
        #region Overrides of MembershipProvider

        #region Constants
        const string MINIMUMPASSWORDLENGTH = "8";
        const string EVENTLOGSOURCE = "Intellinet SharePoint Contact List Membership Provider";
        const string EVENTLOG = "Application";
        const string ERRORMESSAGE = "An exception occurred, causing the system to behave unexpectedly.  Please check the Application event log for more details.";
        #endregion

        #region EventLog Switch
        //
        // If false, exceptions are thrown to the caller. If true,
        // exceptions are written to the event log.
        //
        private bool pWriteExceptionsToEventLog;

        public bool WriteExceptionsToEventLog
        {
            get { return pWriteExceptionsToEventLog; }
            set { pWriteExceptionsToEventLog = value; }
        }
        #endregion

        #region System.Web.MembershipProvider Properties
        //
        // System.Web.Security.MembershipProvider properties.
        //
        private string pApplicationName;
        private bool pEnablePasswordReset;
        private bool pEnablePasswordRetrieval;
        private bool pRequiresQuestionAndAnswer;
        private bool pRequiresUniqueEmail;
        private int pMaxInvalidPasswordAttempts;
        private int pPasswordAttemptWindow;
        private MembershipPasswordFormat pPasswordFormat;

        public override string ApplicationName
        {
            get { return pApplicationName; }
            set { pApplicationName = value; }
        }

        public override bool EnablePasswordReset
        {
            get { return pEnablePasswordReset; }
        }

        public override bool EnablePasswordRetrieval
        {
            get { return pEnablePasswordRetrieval; }
        }

        public override bool RequiresQuestionAndAnswer
        {
            get { return pRequiresQuestionAndAnswer; }
        }

        public override bool RequiresUniqueEmail
        {
            get { return pRequiresUniqueEmail; }
        }

        public override int MaxInvalidPasswordAttempts
        {
            get { return pMaxInvalidPasswordAttempts; }
        }

        public override int PasswordAttemptWindow
        {
            get { return pPasswordAttemptWindow; }
        }

        public override MembershipPasswordFormat PasswordFormat
        {
            get { return pPasswordFormat; }
        }

        private int pMinRequiredNonAlphanumericCharacters = 0;

        public override int MinRequiredNonAlphanumericCharacters
        {
            get { return pMinRequiredNonAlphanumericCharacters; }
        }

        private int pMinRequiredPasswordLength = 2;

        public override int MinRequiredPasswordLength
        {
            get { return pMinRequiredPasswordLength; }
        }

        private string pPasswordStrengthRegularExpression;

        public override string PasswordStrengthRegularExpression
        {
            get { return pPasswordStrengthRegularExpression; }
        }

        public override string Description
        {
            get
            {
                return base.Description;
            }
        }

        public override string Name
        {
            get
            {
                return base.Name;
            }
        }
        #endregion

        #region Custom MembershipProvider properties
        private Guid membershipListID;

        private string pMasterUserName;
        private string pMasterPassword;
        private string pSiteURL;
        private string pMembershipListName;
        private string pUserFieldName;
        private string pPasswordFieldName;

        private SPList pMembershipList;
        private SPWeb mySite;
        private SPSite siteCollection;

        //
        // Used when determining encryption key values.
        //
        private MachineKeySection machineKey;


        public string MasterUserName
        {
            get { return pMasterUserName; }
        }

        public string MasterPassword
        {
            get { return pMasterPassword; }
        }

        public string SiteURL
        {
            get { return pSiteURL; }
        }

        public string ContactMembershipListName
        {
            get { return pMembershipListName; }
        }

        public SPList MembershipContactList
        {
            get { return pMembershipList; }
        }

        public string UserIDFieldName
        {
            get { return pUserFieldName; }
        }

        public string PasswordFieldName
        {
            get { return pPasswordFieldName; }
        }
        #endregion

        #region Helper functions
        //
        // A helper function to retrieve config values from the configuration file.
        //
        private string GetConfigValue(string configValue, string defaultValue)
        {
            if (String.IsNullOrEmpty(configValue))
                return defaultValue;

            return configValue;
        }

        private MembershipUser GetMembershipUserFromListItem(SPListItem userContact)
        {

            bool isApproved, isLockedOut;
            DateTime creationDate, lastLoginDate, lastActivityDate, lastPasswordChangedDate, lastLockedOutDate;

            object providerUserKey = userContact.UniqueId;
            string username = userContact.Fields.GetFieldByInternalName("UserName").GetFieldValueAsText(null);
            string email = userContact.Fields.GetFieldByInternalName("Email").GetFieldValueAsText(null);
            string passwordQuestion = userContact.Fields.GetField("Password Question").GetFieldValueAsText(null);
            string comment = userContact.Fields.GetField("Notes").GetFieldValueAsText(null);
            bool.TryParse(userContact.GetFormattedValue("isApproved"), out isApproved);
            bool.TryParse(userContact.GetFormattedValue("isLockedOut"), out isLockedOut);
            DateTime.TryParse(userContact.GetFormattedValue("Creation Date"), out creationDate);
            DateTime.TryParse(userContact.GetFormattedValue("lastLoginDate"), out lastLoginDate);
            DateTime.TryParse(userContact.GetFormattedValue("lastActivityDate"), out lastActivityDate);
            DateTime.TryParse(userContact.GetFormattedValue("lastPasswordChangedDate"), out lastPasswordChangedDate);
            DateTime.TryParse(userContact.GetFormattedValue("lastLockedOutDate"), out lastLockedOutDate);

            MembershipUser u = new MembershipUser(this.Name,
                                                  username,
                                                  providerUserKey,
                                                  email,
                                                  passwordQuestion,
                                                  comment,
                                                  isApproved,
                                                  isLockedOut,
                                                  creationDate,
                                                  lastLoginDate,
                                                  lastActivityDate,
                                                  lastPasswordChangedDate,
                                                  lastLockedOutDate);

            return u;
        }

        private MembershipUserCollection GetMembershipUsersFromDataTable(DataTable memberTable)
        {
            //doublecheck ordinal values vs column names
            for (int i = 0; i < memberTable.Columns.Count; i++)
            {
                System.Diagnostics.Debug.Print(i.ToString() + " - " + memberTable.Columns[i].ColumnName);
            }
            DataTableReader reader = new DataTableReader(memberTable);
            MembershipUserCollection users = new MembershipUserCollection();

            while (reader.Read())
            {
                MembershipUser u = GetMembershipUserFromDataReader(reader);
                users.Add(u);
            }
            reader.Close();
            reader.Dispose();

            return users;
        }

        private MembershipUser GetMembershipUserFromDataReader(DataTableReader reader)
        {


            object providerUserKey = reader.GetValue((int)reader.GetOrdinal("ID"));
            string username = reader.GetString((int)reader.GetOrdinal("UserName"));
            string email = reader.GetString((int)reader.GetOrdinal("Email"));
            string passwordQuestion = "";
            if (reader.GetValue((int)reader.GetOrdinal("PasswordQuestion")) != DBNull.Value)
                passwordQuestion = reader.GetString((int)reader.GetOrdinal("PasswordQuestion"));
            string comment = "";
            if (reader.GetValue((int)reader.GetOrdinal("Comments")) != DBNull.Value)
                comment = reader.GetString((int)reader.GetOrdinal("Comments"));
            bool isApproved = true;
            if (reader.GetValue((int)reader.GetOrdinal("isApproved")) != DBNull.Value)
                bool.TryParse(reader.GetString((int)reader.GetOrdinal("isApproved")), out isApproved);
            bool isLockedOut = false;
            if (reader.GetValue((int)reader.GetOrdinal("isLockedOut")) != DBNull.Value)
                bool.TryParse(reader.GetString((int)reader.GetOrdinal("isLockedOut")), out isLockedOut);
            DateTime creationDate = new DateTime();
            if (reader.GetValue((int)reader.GetOrdinal("CreationDate")) != DBNull.Value)
                creationDate = reader.GetDateTime((int)reader.GetOrdinal("CreationDate"));
            DateTime lastLoginDate = new DateTime();
            if (reader.GetValue((int)reader.GetOrdinal("lastLoginDate")) != DBNull.Value)
                lastLoginDate = reader.GetDateTime((int)reader.GetOrdinal("lastLoginDate"));
            DateTime lastActivityDate = new DateTime();
            if (reader.GetValue((int)reader.GetOrdinal("lastActivityDate")) != DBNull.Value)
                lastActivityDate = reader.GetDateTime((int)reader.GetOrdinal("lastActivityDate"));
            DateTime lastPasswordChangedDate = new DateTime();
            if (reader.GetValue((int)reader.GetOrdinal("lastPasswordChangeDate")) != DBNull.Value)
                lastPasswordChangedDate = reader.GetDateTime((int)reader.GetOrdinal("lastPasswordChangeDate"));
            DateTime lastLockedOutDate = new DateTime();
            if (reader.GetValue((int)reader.GetOrdinal("lastLockedOutDate")) != DBNull.Value)
                lastLockedOutDate = reader.GetDateTime((int)reader.GetOrdinal("lastLockedOutDate"));

            MembershipUser u = new MembershipUser(this.Name,
                                                  username,
                                                  providerUserKey,
                                                  email,
                                                  passwordQuestion,
                                                  comment,
                                                  isApproved,
                                                  isLockedOut,
                                                  creationDate,
                                                  lastLoginDate,
                                                  lastActivityDate,
                                                  lastPasswordChangedDate,
                                                  lastLockedOutDate);
            return u;
        }

        private MembershipUserCollection GetMembershipUsersFromDataTable(DataView filteredMemberTable)
        {
            //DataTable filterResults = filteredMemberTable.ToTable();
            return GetMembershipUsersFromDataTable(filteredMemberTable.ToTable());
        }

        //
        // UpdateFailureCount
        //   A helper method that performs the checks and updates associated with
        // password failure tracking.
        //

        private void UpdateFailureCount(string username, string failureType)
        {
            // not tracking failed password attempts in this version
        }


        //
        // EncodePassword
        //   Encrypts, Hashes, or leaves the password clear based on the PasswordFormat.
        //
        private string EncodePassword(string password)
        {
            string encodedPassword = password;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    encodedPassword =
                      Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password)));
                    break;
                case MembershipPasswordFormat.Hashed:
                    encodedPassword = HashPassword(password);
                    break;
                default:
                    throw new ProviderException("Unsupported password format.");
            }

            return encodedPassword;
        }

        private string HashPassword(string password)
        {
            HMACSHA1 hash = new HMACSHA1();
            hash.Key = HexToByte(machineKey.ValidationKey);
            string encodedPassword =
              Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
            return encodedPassword;
        }

        //
        // UnEncodePassword
        //   Decrypts or leaves the password clear based on the PasswordFormat.
        //
        private string UnEncodePassword(string encodedPassword)
        {
            string password = encodedPassword;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    password =
                      Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password)));
                    break;
                case MembershipPasswordFormat.Hashed:
                    // can't unencode a hashed password, so return the hashed value of the plaintext password.
                    // password = HashPassword(encodedPassword);
                    break;

                default:
                    throw new ProviderException("Unsupported password format.");
            }

            return password;
        }

        //
        // HexToByte
        //   Converts a hexadecimal string to a byte array. Used to convert encryption
        // key values from the configuration.
        //
        private byte[] HexToByte(string hexString)
        {
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            return returnBytes;
        }

        /// <summary>
        /// TODO: This code shouldn't be needed in every function
        /// </summary>
        /// <returns></returns>
        private SPList InitConnection()
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                try
                {
                    //
                    // Initialize SharePoint list connection.
                    //                    
                    siteCollection = new SPSite(pSiteURL);
                    mySite = siteCollection.OpenWeb();
                    mySite.AllowUnsafeUpdates = true;
                    pMembershipList = mySite.Lists[pMembershipListName];
                    membershipListID = pMembershipList.ID;

                }
                catch (Exception ex)
                {
                    throw new Exception("The system failed to correctly retrieve the membership list " + ex.Message);
                }
            });
            return pMembershipList;
        }

        private void DisposeConnection()
        {
            mySite.Dispose();
            siteCollection.Dispose();
            membershipListID = Guid.Empty;
        }
        #endregion

        #region Methods

        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            try
            {

                SPSecurity.RunWithElevatedPrivileges(delegate()
                {

                    //
                    // Initialize values from web.config.
                    //
                    if (config == null)
                        throw new ArgumentNullException("config");

                    if (name == null || name.Length == 0)
                        name = "CsMembershipProvider";

                    // Initialize the abstract base class.  
                    base.Initialize(name, config);
                    // base provider required properties
                    pApplicationName = GetConfigValue(config["applicationName"], System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
                    pMaxInvalidPasswordAttempts = Convert.ToInt32(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
                    pPasswordAttemptWindow = Convert.ToInt32(GetConfigValue(config["passwordAttemptWindow"], "10"));
                    pMinRequiredNonAlphanumericCharacters = Convert.ToInt32(GetConfigValue(config["minRequiredNonAlphanumericCharacters"], "1"));
                    pMinRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], MINIMUMPASSWORDLENGTH));
                    pPasswordStrengthRegularExpression = Convert.ToString(GetConfigValue(config["passwordStrengthRegularExpression"], ""));
                    pEnablePasswordReset = Convert.ToBoolean(GetConfigValue(config["enablePasswordReset"], "true"));
                    pEnablePasswordRetrieval = Convert.ToBoolean(GetConfigValue(config["enablePasswordRetrieval"], "true"));
                    pRequiresQuestionAndAnswer = Convert.ToBoolean(GetConfigValue(config["requiresQuestionAndAnswer"], "false"));
                    pRequiresUniqueEmail = Convert.ToBoolean(GetConfigValue(config["requiresUniqueEmail"], "true"));
                    pWriteExceptionsToEventLog = Convert.ToBoolean(GetConfigValue(config["writeExceptionsToEventLog"], "true"));
                    // custom provider properties
                    pMasterUserName = GetConfigValue(config["masterUserName"], "AdminSystem");
                    pMasterPassword = GetConfigValue(config["masterUserPassword"], "M@nhDung");
                    pSiteURL = GetConfigValue(config["SiteURL"], "");
                    pMembershipListName = GetConfigValue(config["MembershipList"], "");
                    pUserFieldName = GetConfigValue(config["userIDFieldName"], "Email");
                    pPasswordFieldName = GetConfigValue(config["passwordFieldName"], "Password");

                    string temp_format = config["passwordFormat"];
                    if (temp_format == null)
                    {
                        temp_format = "Hashed";
                    }

                    switch (temp_format)
                    {
                        case "Hashed":
                            pPasswordFormat = MembershipPasswordFormat.Hashed;
                            break;
                        case "Encrypted":
                            pPasswordFormat = MembershipPasswordFormat.Encrypted;
                            break;
                        case "Clear":
                            pPasswordFormat = MembershipPasswordFormat.Clear;
                            break;
                        default:
                            throw new ProviderException("Password format not supported.");
                    }


                    // Get encryption and decryption key information from the configuration.
                    Configuration cfg =
                      WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
                    machineKey = (MachineKeySection)cfg.GetSection("system.web/machineKey");

                    if (machineKey.ValidationKey.Contains("AutoGenerate"))
                        if (PasswordFormat != MembershipPasswordFormat.Clear)
                            throw new ProviderException("Hashed or Encrypted passwords " +
                                                        "are not supported with auto-generated keys.");

                    //pMembershipList = InitConnection();

                });
            }
            catch
            {
                base.Initialize(name, config);
            }
        }

        public override bool ValidateUser(string username, string password)
        {
            bool bValidMember = false;

            bool isMasterUser = false;
            if (pMasterUserName == username)
            {
                if (pMasterPassword == password)
                {
                    isMasterUser = true;
                }
                else
                {
                    isMasterUser = false;
                }
            }
            else { isMasterUser = false; }
            if (isMasterUser == true) { return true; }

            try
            {
                // handle hashed and encoded passwords
                string encodedPassword = string.Empty;
                if (PasswordFormat == MembershipPasswordFormat.Hashed)
                {
                    // can't unencode a hashed password so instead take the user's input and hash it to see
                    // if it matches what's stored in the password list
                    encodedPassword = HashPassword(password);
                }
                else if (PasswordFormat == MembershipPasswordFormat.Encrypted)
                {
                    // rather than decrypt what's in the list, just encrypt the user input to see if it matches
                    // what's in the list
                    encodedPassword = EncodePassword(password);
                }
                else { encodedPassword = password; }

                SPListItemCollection validMembers = FindUserByUsernameAndPassword(username, encodedPassword);

                if (null != validMembers)
                {

                    if (0 == validMembers.Count)
                    {
                        // found nothing.  Check with the unencrypted password to validate that the problem isn't that the password is being
                        // stored in an unencrypted state due to being added via the List itself
                        validMembers = FindUserByUsernameAndPassword(username, password);
                        if (null == validMembers)
                        {
                            // couldn't find anything with either the encrypted or the unencrypted password format.  Must be a 
                            // user error
                            bValidMember = false;
                        }
                    }

                    // found one row - make sure the user is authenticated
                    if (validMembers.Count == 1)
                    {
                        foreach (SPListItem userRow in validMembers)
                        {
                            // ensure that the user is authenticated
                            string delimiter = "' ";
                            string authenticatedField = "ows_isApproved='";

                            // find ows_isApproved in the userID.XML
                            int startIndex = userRow.Xml.IndexOf(authenticatedField);
                            if (0 > startIndex)
                            {
                                // the field doesn't exist in the data which means it hasn't been set, return false
                                bValidMember = false;
                            }
                            else
                            {
                                int endIndex = userRow.Xml.IndexOf(delimiter, startIndex);
                                string validMemberValue = userRow.Xml.Substring((startIndex + authenticatedField.Length),
                                                                              (endIndex - (startIndex + authenticatedField.Length)));
                                if (validMemberValue == "0")
                                    bValidMember = false;
                                else
                                    bValidMember = true;
                            }

                        }
                    }
                    else // returned more than one row with that e-mail address and password - this is an error condition
                    {
                        bValidMember = false;
                    }
                }

                return bValidMember;

            }
            catch (Exception e)
            {
                throw new ProviderException("The ValidateUser function has thrown the following error " + e.Message);
            }
        }

        private SPListItemCollection FindUserByUsernameAndPassword(string username, string password)
        {
            string query = string.Format("<Where><And><Eq><FieldRef Name='{0}'/><Value Type='Text'>{1}</Value></Eq><Eq><FieldRef Name='{2}'/><Value Type='Text'>{3}</Value></Eq></And></Where>", pUserFieldName, username, pPasswordFieldName, password);
            SPQuery memberSearch = new SPQuery();
            memberSearch.Query = query;

            SPList membershipList = InitConnection();
            SPListItemCollection validMembers = membershipList.GetItems(memberSearch);
            DisposeConnection();
            return validMembers;
        }

        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            SPList membershipList = InitConnection();
            DataTable memberTable = membershipList.Items.GetDataTable();
            DataView filterUser = new DataView(memberTable);
            filterUser.RowFilter = "Email LIKE '%" + emailToMatch + "%'";
            totalRecords = filterUser.Count;
            MembershipUserCollection users = GetMembershipUsersFromDataTable(filterUser);

            filterUser.Dispose();
            memberTable.Dispose();

            DisposeConnection();
            return users;
        }


        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            SPList membershipList = InitConnection();
            DataTable memberTable = membershipList.Items.GetDataTable();
            DataView filterUser = new DataView(memberTable);
            filterUser.RowFilter = "Email LIKE '%" + usernameToMatch + "%'";
            totalRecords = filterUser.Count;
            MembershipUserCollection users = GetMembershipUsersFromDataTable(filterUser);

            filterUser.Dispose();
            memberTable.Dispose();

            DisposeConnection();
            return users;
        }

        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            SPList membershipList = InitConnection();
            DataTable memberTable = new DataTable();
            try
            {
                totalRecords = membershipList.Items.Count;
                memberTable = membershipList.Items.GetDataTable();
                MembershipUserCollection users = GetMembershipUsersFromDataTable(memberTable);

                memberTable.Dispose();

                DisposeConnection();
                return users;
            }
            catch (Exception e)
            {
                memberTable.Dispose();

                DisposeConnection();
                throw new Exception("The method GetAllUsers threw an error: " + e.Message);
            }
        }

        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            SPList membershipList = InitConnection();

            try
            {
                DataTable memberTable = new DataTable();
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    memberTable = membershipList.Items.GetDataTable();
                });
                DataView filterUser = new DataView(memberTable)
                                          {
                                              RowFilter = "ID = '" + providerUserKey.ToString() + "'"
                                          };

                int totalRecords = filterUser.Count;
                if (0 == totalRecords)
                {
                    // can't find the user
                    return null;
                }
                if (2 >= totalRecords)
                {
                    // found the user
                    DataTable filterTable = filterUser.ToTable();
                    DataTableReader filterReader = new DataTableReader(filterTable);
                    filterReader.Read(); // read the first record
                    MembershipUser user = GetMembershipUserFromDataReader(filterReader);

                    filterReader.Close();
                    filterReader.Dispose();
                    filterTable.Dispose();
                    filterUser.Dispose();
                    memberTable.Dispose();

                    DisposeConnection();
                    return user;
                }
                else
                {
                    DisposeConnection();
                    throw new Exception("The GetUser method or operation returned more than one user, and this is a error condition.");
                }

            }
            catch
            {
                DisposeConnection();
                throw new Exception("The method GetUser(object, bool) threw an error attempting to search for the user " + providerUserKey.ToString());
            }
        }

        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            SPList membershipList = InitConnection();

            try
            {
                DataTable memberTable = new DataTable();
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    memberTable = membershipList.Items.GetDataTable();
                });
                DataView filterUser = new DataView(memberTable);
                filterUser.RowFilter = "UserName = '" + username.ToString() + "'";

                int totalRecords = filterUser.Count;
                if (0 == totalRecords)
                {
                    // can't find the user
                    return null;
                }
                if (totalRecords <= 2)
                {
                    // found the user
                    DataTable filterTable = filterUser.ToTable();
                    DataTableReader filterReader = new DataTableReader(filterTable);
                    filterReader.Read(); // read the first record
                    MembershipUser user = GetMembershipUserFromDataReader(filterReader);

                    filterReader.Close();
                    filterReader.Dispose();
                    filterTable.Dispose();
                    filterUser.Dispose();
                    memberTable.Dispose();

                    DisposeConnection();
                    return user;
                }
                else
                {
                    DisposeConnection();
                    throw new Exception("The GetUser method or operation returned more than one user, and this is a error condition.");
                }
            }
            catch (Exception e)
            {
                DisposeConnection();
                throw new Exception("The method GetUser(string, bool) threw an error attempting to search for the user " + username + ", Error: " + e.Message);
            }
        }

        public override string GetUserNameByEmail(string email)
        {
            SPList membershipList = InitConnection();

            try
            {
                DataTable memberTable = new DataTable();
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    memberTable = membershipList.Items.GetDataTable();
                });


                DataView filterUser = new DataView(memberTable);
                filterUser.RowFilter = "Email = '" + email + "'";

                int totalRecords = filterUser.Count;
                if (0 == totalRecords)
                {
                    return string.Empty;
                }
                if (2 >= totalRecords)
                {
                    DataTable filterTable = filterUser.ToTable();
                    DataTableReader filterReader = new DataTableReader(filterTable);
                    filterReader.Read();

                    filterUser.Dispose();
                    memberTable.Dispose();

                    DisposeConnection();

                    if (System.DBNull.Value == filterReader.GetValue((int)filterReader.GetOrdinal("FullName")))
                    {
                        return email;
                    }
                    else
                    {

                        return filterReader.GetString((int)filterReader.GetOrdinal("FullName"));
                    }
                }
                else
                {
                    DisposeConnection();
                    throw new Exception("The GetUser method or operation returned more than one user, and this is a error condition.");
                }
            }
            catch (Exception e)
            {
                DisposeConnection();
                throw new Exception("The method GetUserNamebyEmail threw an error attempting to search for the user " + email + ", Error: " + e.Message);
            }
        }

        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            SPList membershipList = InitConnection();

            // validation prior to creating list entry
            var args = new ValidatePasswordEventArgs(username, password, true);
            OnValidatingPassword(args);
            if (args.Cancel)
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            if (RequiresUniqueEmail && GetUserNameByEmail(email) != "")
            {
                status = MembershipCreateStatus.DuplicateEmail;
                return null;
            }

            ////TODO:Insert e-mail validation rules
            //if (InvalidEmailDomain())
            //{
            //    status = MembershipCreateStatus.UserRejected;
            //    return null;
            //}

            MembershipUser u = GetUser(email, false);
            if (u == null)
            {
                if (providerUserKey == null)
                {
                    providerUserKey = Guid.NewGuid();
                }
                else
                {
                    if (!(providerUserKey is Guid))
                    {
                        status = MembershipCreateStatus.InvalidProviderUserKey;
                        return null;
                    }
                }

                SPListItemCollection userListEntries = membershipList.Items;
                try
                {
                    DateTime createDate = DateTime.Now;
                    SPListItem newUser = userListEntries.Add();

                    newUser["UserID"] = providerUserKey;
                    newUser["Title"] = username;
                    newUser["Email"] = email;
                    newUser["UserName"] = username;
                    newUser["Password"] = EncodePassword(password);
                    newUser["PasswordQuestion"] = passwordQuestion;
                    newUser["PasswordReminderAnswer"] = EncodePassword(passwordAnswer);
                    newUser["isApproved"] = isApproved;
                    newUser["isLockedOut"] = false;
                    newUser["CreationDate"] = createDate;
                    newUser["lastPasswordChangeDate"] = createDate;
                    newUser["lastActivityDate"] = createDate;
                    newUser["lastLockedOutDate"] = createDate;
                    newUser["Role"] = "Employee";

                    // persist changes to the SharePoint content database
                    newUser.Update();

                    status = MembershipCreateStatus.Success;
                    return GetUser(email, false);
                }
                catch
                {
                    status = MembershipCreateStatus.ProviderError;
                }
            }
            else
            {
                status = MembershipCreateStatus.DuplicateUserName;
            }

            DisposeConnection();
            return null;

        }

        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            bool returnValue = false;

            SPList membershipList = InitConnection();
            try
            {
                // find the user's row
                SPQuery query = new SPQuery();
                query.Query = String.Format("<Where><Eq><FieldRef ProductName='UserName'/><Value Type='Text'>{0}</Value></Eq></Where>", username);

                SPListItemCollection listItems = membershipList.GetItems(query);

                if (0 == listItems.Count)
                {
                    // return something that means do nothing - no user found
                    returnValue = false;
                }
                if (1 == listItems.Count)
                {
                    try
                    {
                        foreach (SPListItem userRow in listItems)
                        {
                            string delimiter = "' ";
                            string passwordField = "ows_Password='";

                            // find ows_Password in the userID.XML
                            int startIndex = userRow.Xml.IndexOf(passwordField);
                            int endIndex = userRow.Xml.IndexOf(delimiter, startIndex);
                            string encryptedPassword = userRow.Xml.Substring((startIndex + passwordField.Length),
                                                                          (endIndex - (startIndex + passwordField.Length)));

                            string currentPassword = string.Empty;
                            // check to see if the encrypted password really isn't encrypted and it matches the old password
                            if (encryptedPassword == oldPassword)
                            {
                                // the passwords match - might be due to a user added directly to the list
                                currentPassword = encryptedPassword;
                            }
                            else
                            {
                                if (PasswordFormat == MembershipPasswordFormat.Hashed)
                                {
                                    // can't unencode hashed passwords so instead hash the user entered value to see if
                                    // they match
                                    oldPassword = HashPassword(oldPassword);
                                    currentPassword = encryptedPassword;
                                }
                                else
                                {
                                    currentPassword = UnEncodePassword(encryptedPassword);
                                }
                            }

                            if (currentPassword == oldPassword)
                            {
                                // update the list to have the new password
                                userRow["Password"] = EncodePassword(newPassword);
                                userRow["lastPasswordChangeDate"] = DateTime.Now.ToString();
                                userRow.Update();
                                return true;
                            }
                            else
                            {
                                // check to see if the passwords are supposed to be hashed but aren't

                                return false;
                            }

                        }
                    }
                    catch
                    {
                        // errors here mean data hasn't been entered for the user - return false
                        returnValue = false;
                    }
                }
                else // (2 >= listItems.Count)
                {
                    returnValue = false;
                }
                DisposeConnection();
                return returnValue;
            }
            catch (Exception e)
            {
                DisposeConnection();
                throw new Exception("Unable to change the user's password due to the following error: " + e.Message);
            }
        }

        public override string ResetPassword(string username, string answer)
        {
            if (!EnablePasswordReset)
            {
                throw new NotSupportedException("Password reset is not enabled.");
            }

            if (answer == null && RequiresQuestionAndAnswer)
            {
                UpdateFailureCount(username, "passwordAnswer");

                throw new ProviderException("Password answer required for password reset.");
            }

            string newPassword =
              System.Web.Security.Membership.GeneratePassword(MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters);


            ValidatePasswordEventArgs args =
              new ValidatePasswordEventArgs(username, newPassword, true);

            OnValidatingPassword(args);

            if (args.Cancel)
                if (args.FailureInformation != null)
                    throw args.FailureInformation;
                else
                    throw new MembershipPasswordException("Reset password canceled due to password validation failure.");


            // connect to the list and update the password
            SPList membershipList = InitConnection();
            try
            {
                // find the user's row
                SPQuery query = new SPQuery();
                query.Query = String.Format("<Where><Eq><FieldRef ProductName='Email'/><Value Type='Text'>{0}</Value></Eq></Where>", username);

                SPListItemCollection listItems = membershipList.GetItems(query);

                if (0 == listItems.Count)
                {
                    // return something that means do nothing - no user found
                    DisposeConnection();
                    throw new MembershipPasswordException("User not found, or user is locked out. Password not Reset.");
                }
                if (1 == listItems.Count)
                {
                    try
                    {
                        foreach (SPListItem userRow in listItems)
                        {
                            // update the list to have the new password
                            userRow["Password"] = EncodePassword(newPassword);
                            userRow["lastPasswordChangeDate"] = DateTime.Now.ToString();
                            userRow.Update();
                        }
                    }
                    catch (Exception e)
                    {
                        // errors here mean data hasn't been entered for the user - return false
                        DisposeConnection();
                        throw new Exception("Unable to change the user's password due to the following error: " + e.Message);
                    }
                }
                else // (2 >= listItems.Count)
                {
                    DisposeConnection();
                    throw new MembershipPasswordException("Multiple users with the same e-mail address found. Password not Reset.");
                }
                DisposeConnection();
                return newPassword;
            }
            catch (Exception e)
            {
                DisposeConnection();
                throw new Exception("Unable to change the user's password due to the following error: " + e.Message);
            }
        }

        #endregion

        #region Not Implemented until necessary

        protected override byte[] DecryptPassword(byte[] encodedPassword)
        {
            return base.DecryptPassword(encodedPassword);
        }

        protected override byte[] EncryptPassword(byte[] password)
        {
            return base.EncryptPassword(password);
        }

        public override string GetPassword(string username, string answer)
        {
            throw new Exception("The method or operation GetPassword is not implemented.");
        }

        protected override void OnValidatingPassword(ValidatePasswordEventArgs e)
        {
            base.OnValidatingPassword(e);
        }


        public override bool UnlockUser(string userName)
        {
            throw new Exception("The method or operation UnlockUser is not implemented.");
        }

        public override void UpdateUser(MembershipUser user)
        {
            throw new Exception("The method or operation UpdateUser is not implemented.");
        }
        #endregion

        #region Deliberately Not Implemented
        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            throw new Exception("The method or operation ChangePasswordQuestionAndAnswer is deliberately not implemented in this provider as the functionality is expected to be handled in the SharePoint list.");
        }

        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            throw new Exception("The method or operation DeleteUser is deliberately not implemented in this provider as the functionality is expected to be handled in the SharePoint list.");
        }

        public override int GetNumberOfUsersOnline()
        {
            throw new Exception("The method or operation GetNumberOfUsersOnline is not implemented.");
        }
        #endregion

        #endregion
    }
}
